<!--
 * @Descripttion: 资源文件选择器
 * @version: 1.0
 * @Author: sakuya
 * @Date: 2021年10月11日16:01:40
 * @LastEditors: Xujianchen
 * @LastEditTime: 2023-03-17 10:56:47
-->

<template>
	<div class="sc-file-select">
		<div v-loading="menuLoading" class="sc-file-select__side">
			<div class="sc-file-select__side-menu">
				<el-tree
					ref="group"
					class="menu"
					:data="menu"
					:node-key="treeProps.key"
					:props="treeProps"
					:current-node-key="menu.length > 0 ? menu[0][treeProps.key] : ''"
					highlight-current
					@node-click="groupClick"
				>
					<template #default="{ node }">
						<span class="el-tree-node__label">
							<el-icon class="icon"><el-icon-folder /></el-icon>{{ node.label }}
						</span>
					</template>
				</el-tree>
			</div>
			<div v-if="multiple" class="sc-file-select__side-msg">
				已选择 <b>{{ value.length }}</b> / <b>{{ max }}</b> 项
			</div>
		</div>
		<div v-loading="listLoading" class="sc-file-select__files">
			<div class="sc-file-select__top">
				<div v-if="!hideUpload" class="upload">
					<el-upload
						class="sc-file-select__upload"
						action=""
						multiple
						:show-file-list="false"
						:accept="accept"
						:on-change="uploadChange"
						:before-upload="uploadBefore"
						:on-progress="uploadProcess"
						:on-success="uploadSuccess"
						:on-error="uploadError"
						:http-request="uploadRequest"
					>
						<el-button type="primary" icon="el-icon-upload">本地上传</el-button>
					</el-upload>
					<span class="tips"
						><el-icon><el-icon-warning /></el-icon>大小不超过{{ maxSize }}MB</span
					>
				</div>
				<div class="keyword">
					<el-input
						v-model="keyword"
						prefix-icon="el-icon-search"
						placeholder="文件名搜索"
						clearable
						@keyup.enter="search"
						@clear="search"
					></el-input>
				</div>
			</div>
			<div class="sc-file-select__list">
				<el-scrollbar ref="scrollbar">
					<el-empty v-if="fileList.length === 0 && data.length == 0" description="无数据" :image-size="80"></el-empty>
					<div v-for="(file, index) in fileList" :key="index" class="sc-file-select__item">
						<div class="sc-file-select__item__file">
							<div class="sc-file-select__item__upload">
								<el-progress type="circle" :percentage="file.progress" :width="70"></el-progress>
							</div>
							<el-image :src="file.tempImg" fit="contain"></el-image>
						</div>
						<p>{{ file.name }}</p>
					</div>
					<div
						v-for="item in data"
						:key="item[fileProps.key]"
						class="sc-file-select__item"
						:class="{ active: value.includes(item[fileProps.url]) }"
						@click="select(item)"
					>
						<div class="sc-file-select__item__file">
							<div v-if="multiple" class="sc-file-select__item__checkbox">
								<el-icon><el-icon-check /></el-icon>
							</div>
							<div v-else class="sc-file-select__item__select">
								<el-icon><el-icon-check /></el-icon>
							</div>
							<div class="sc-file-select__item__box"></div>
							<el-image v-if="_isImg(item[fileProps.url])" :src="item[fileProps.url]" fit="contain" lazy></el-image>
							<div v-else class="item-file item-file-doc">
								<i
									v-if="files[_getExt(item[fileProps.url])]"
									:class="files[_getExt(item[fileProps.url])].icon"
									:style="{ color: files[_getExt(item[fileProps.url])].color }"
								></i>
								<i v-else class="sc-icon-file-list-fill" style="color: #999"></i>
							</div>
						</div>
						<p :title="item[fileProps.fileName]">{{ item[fileProps.fileName] }}</p>
					</div>
				</el-scrollbar>
			</div>
			<div class="sc-file-select__pagination">
				<el-pagination
					v-model:currentPage="currentPage"
					small
					background
					layout="prev, pager, next"
					:total="total"
					:page-size="pageSize"
					@current-change="reload"
				></el-pagination>
			</div>
			<div class="sc-file-select__do">
				<slot name="do"></slot>
				<el-button type="primary" :disabled="value.length <= 0" @click="submit">确 定</el-button>
			</div>
		</div>
	</div>
</template>

<script>
import config from '@/config/fileSelect'

export default {
	props: {
		modelValue: null,
		hideUpload: { type: Boolean, default: false },
		multiple: { type: Boolean, default: false },
		max: { type: Number, default: config.max },
		onlyImage: { type: Boolean, default: false },
		maxSize: { type: Number, default: config.maxSize }
	},
	data() {
		return {
			keyword: null,
			pageSize: 20,
			total: 0,
			currentPage: 1,
			data: [],
			menu: [],
			menuId: '',
			value: this.multiple ? [] : '',
			fileList: [],
			accept: this.onlyImage ? 'image/gif, image/jpeg, image/png' : '',
			listLoading: false,
			menuLoading: false,
			treeProps: config.menuProps,
			fileProps: config.fileProps,
			files: config.files
		}
	},
	watch: {
		multiple() {
			this.value = this.multiple ? [] : ''
			this.$emit('update:modelValue', JSON.parse(JSON.stringify(this.value)))
		}
	},
	mounted() {
		this.getMenu()
		this.getData()
	},
	methods: {
		//获取分类数据
		async getMenu() {
			this.menuLoading = true
			var res = await config.menuApiObj.get()
			this.menu = res.data
			this.menuLoading = false
		},
		//获取列表数据
		async getData() {
			this.listLoading = true
			var reqData = {
				[config.request.menuKey]: this.menuId,
				[config.request.page]: this.currentPage,
				[config.request.pageSize]: this.pageSize,
				[config.request.keyword]: this.keyword
			}
			if (this.onlyImage) {
				reqData.type = 'image'
			}
			var res = await config.listApiObj.get(reqData)
			var parseData = config.listParseData(res)
			this.data = parseData.rows
			this.total = parseData.total
			this.listLoading = false
			this.$refs.scrollbar.setScrollTop(0)
		},
		//树点击事件
		groupClick(data) {
			this.menuId = data.id
			this.currentPage = 1
			this.keyword = null
			this.getData()
		},
		//分页刷新表格
		reload() {
			this.getData()
		},
		search() {
			this.currentPage = 1
			this.getData()
		},
		select(item) {
			const itemUrl = item[this.fileProps.url]
			if (this.multiple) {
				if (this.value.includes(itemUrl)) {
					this.value.splice(
						this.value.findIndex((f) => f === itemUrl),
						1
					)
				} else {
					this.value.push(itemUrl)
				}
			} else {
				if (this.value.includes(itemUrl)) {
					this.value = ''
				} else {
					this.value = itemUrl
				}
			}
		},
		submit() {
			const value = JSON.parse(JSON.stringify(this.value))
			this.$emit('update:modelValue', value)
			this.$emit('submit', value)
		},
		//上传处理
		uploadChange(file, fileList) {
			file.tempImg = URL.createObjectURL(file.raw)
			this.fileList = fileList
		},
		uploadBefore(file) {
			const maxSize = file.size / 1024 / 1024 < this.maxSize
			if (!maxSize) {
				this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`)
				return false
			}
		},
		uploadRequest(param) {
			var apiObj = config.apiObj
			const data = new FormData()
			data.append('file', param.file)
			data.append([config.request.menuKey], this.menuId)
			apiObj
				.post(data, {
					onUploadProgress: (e) => {
						param.onProgress(e)
					}
				})
				.then((res) => {
					param.onSuccess(res)
				})
				.catch((err) => {
					param.onError(err)
				})
		},
		uploadProcess(event, file) {
			file.progress = Number(((event.loaded / event.total) * 100).toFixed(2))
		},
		uploadSuccess(res, file) {
			this.fileList.splice(
				this.fileList.findIndex((f) => f.uid === file.uid),
				1
			)
			var response = config.uploadParseData(res)
			this.data.unshift({
				[this.fileProps.key]: response.id,
				[this.fileProps.fileName]: response.fileName,
				[this.fileProps.url]: response.url
			})
			if (!this.multiple) {
				this.value = response.url
			}
		},
		uploadError(err) {
			this.$notify.error({
				title: '上传文件错误',
				message: err
			})
		},
		//内置函数
		_isImg(fileUrl) {
			const imgExt = ['.jpg', '.jpeg', '.png', '.gif', '.bmp']
			const fileExt = fileUrl.substring(fileUrl.lastIndexOf('.'))
			return imgExt.indexOf(fileExt) !== -1
		},
		_getExt(fileUrl) {
			return fileUrl.substring(fileUrl.lastIndexOf('.') + 1)
		}
	}
}
</script>

<style scoped>
.sc-file-select {
	display: flex;
}
.sc-file-select__files {
	flex: 1;
}

.sc-file-select__list {
	height: 400px;
}
.sc-file-select__item {
	display: inline-block;
	margin: 0 15px 25px 0;
	width: 110px;
	cursor: pointer;
}
.sc-file-select__item__file {
	width: 110px;
	height: 110px;
	position: relative;
}
.sc-file-select__item__file .el-image {
	width: 110px;
	height: 110px;
}
.sc-file-select__item__box {
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	border: 2px solid var(--el-color-success);
	z-index: 1;
	display: none;
}
.sc-file-select__item__box::before {
	content: '';
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	background: var(--el-color-success);
	opacity: 0.2;
	display: none;
}
.sc-file-select__item:hover .sc-file-select__item__box {
	display: block;
}
.sc-file-select__item.active .sc-file-select__item__box {
	display: block;
}
.sc-file-select__item.active .sc-file-select__item__box::before {
	display: block;
}
.sc-file-select__item p {
	margin-top: 10px;
	white-space: nowrap;
	text-overflow: ellipsis;
	overflow: hidden;
	-webkit-text-overflow: ellipsis;
	text-align: center;
}
.sc-file-select__item__checkbox {
	position: absolute;
	width: 20px;
	height: 20px;
	top: 7px;
	right: 7px;
	z-index: 2;
	background: rgba(0, 0, 0, 0.2);
	border: 1px solid #fff;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
}
.sc-file-select__item__checkbox i {
	font-size: 14px;
	color: #fff;
	font-weight: bold;
	display: none;
}
.sc-file-select__item__select {
	position: absolute;
	width: 20px;
	height: 20px;
	top: 0px;
	right: 0px;
	z-index: 2;
	background: var(--el-color-success);
	display: none;
	flex-direction: column;
	align-items: center;
	justify-content: center;
}
.sc-file-select__item__select i {
	font-size: 14px;
	color: #fff;
	font-weight: bold;
}
.sc-file-select__item.active .sc-file-select__item__checkbox {
	background: var(--el-color-success);
}
.sc-file-select__item.active .sc-file-select__item__checkbox i {
	display: block;
}
.sc-file-select__item.active .sc-file-select__item__select {
	display: flex;
}
.sc-file-select__item__file .item-file {
	width: 110px;
	height: 110px;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
}
.sc-file-select__item__file .item-file i {
	font-size: 40px;
}
.sc-file-select__item__file .item-file.item-file-doc {
	color: #409eff;
}

.sc-file-select__item__upload {
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: 1;
	background: rgba(255, 255, 255, 0.7);
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
}

.sc-file-select__side {
	width: 200px;
	margin-right: 15px;
	border-right: 1px solid rgba(128, 128, 128, 0.2);
	display: flex;
	flex-flow: column;
}
.sc-file-select__side-menu {
	flex: 1;
}
.sc-file-select__side-msg {
	height: 32px;
	line-height: 32px;
}

.sc-file-select__top {
	margin-bottom: 15px;
	display: flex;
	justify-content: space-between;
}
.sc-file-select__upload {
	display: inline-block;
}
.sc-file-select__top .tips {
	font-size: 12px;
	margin-left: 10px;
	color: #999;
}
.sc-file-select__top .tips i {
	font-size: 14px;
	margin-right: 5px;
	position: relative;
	bottom: -0.125em;
}
.sc-file-select__pagination {
	margin: 15px 0;
}

.sc-file-select__do {
	text-align: right;
}
</style>
